#include "stdafx.h"
#include "CppUnitTest.h"

#include <vex/iterable/concept/range_concepts.h>
#include <vex/iterable/any_range.h>
#include <vex/iterable/make_any_range.h>
#include <vex.tests/mstest/tests.range/tests.range.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests {	namespace range_any_intellisense {

    using namespace boost::assign;

    namespace vi = vex::iterable;

    TEST_CLASS(test_class)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.range.any.intellisense")
            END_TEST_CLASS_ATTRIBUTE();

        static const std::size_t m_container_size = 4u;
        typedef std::vector<int> container_t;
        typedef container_t::iterator iterator_t;
        typedef container_t::const_iterator const_iterator_t;

        container_t 
            m_container, 
            m_container_same_size_different_values, 
            m_container_empty, 
            m_container_with_one_element;

        VEX_TESTS_TRACKED_MEM_CHECK();
    public:
        TEST_METHOD_INITIALIZE(set_up)
        {
            VEX_TESTS_TRACKED_MEM_CHECK_START();

            container_t().swap(m_container);
            container_t().swap(m_container_same_size_different_values);
            container_t().swap(m_container_empty);
            container_t().swap(m_container_with_one_element);

            m_container += 1, 2, 3, 4, 5;
            m_container_same_size_different_values += 6, 7, 8, 9, 5;
            m_container_with_one_element += 1;
        }

        TEST_METHOD_CLEANUP(tear_down)
        {
            container_t().swap(m_container);
            container_t().swap(m_container_same_size_different_values);
            container_t().swap(m_container_empty);
            container_t().swap(m_container_with_one_element);

            VEX_TESTS_TRACKED_MEM_CHECK_FINISH();
        }

        TEST_METHOD(as_any_functions_are_found_and_applied_correctly)
        {
            {
                auto r = adl_as_any_from_out(
                    vex::iterable::make_raw_range(
                    vex::iterable::make_output_range<int>(
                    std::back_inserter(m_container))));
                Assert::IsTrue(r);
                auto t_old_size = m_container.size();
                r->put(1);
                Assert::AreEqual(1u + t_old_size, m_container.size());
                Assert::AreEqual(1, m_container.back());
            }

            {
                auto r = vex::as_any_from_output() | 
                    vex::iterable::make_raw_range(
                    vex::iterable::make_output_range<int>(
                    std::back_inserter(m_container)));
                Assert::IsTrue(r);
                auto t_old_size = m_container.size();
                r->put(1);
                Assert::AreEqual(1u + t_old_size, m_container.size());
                Assert::AreEqual(1, m_container.back());
            }

            {
                auto r = vex::as_any_from_out(
                    vex::iterable::make_raw_range(
                    vex::iterable::make_output_range<int>(
                    std::back_inserter(m_container))));
                Assert::IsTrue(r);
                auto t_old_size = m_container.size();
                r->put(1);
                Assert::AreEqual(1u + t_old_size, m_container.size());
                Assert::AreEqual(1, m_container.back());
            }

            {
                auto ar = vex::as_any_from_output() 
                    | vex::iterable::make_raw_range(vex::iterable::make_range(m_container));
                Assert::IsTrue(ar);
            }

            {
                auto ar = adl_as_any_from_out(
                    vex::iterable::make_raw_range(vex::iterable::make_range(m_container)));
                Assert::IsTrue(ar);
            }

            {
                auto ar = vex::as_any_from_out(
                    (vex::iterable::contract::range<vex::iterable::random_access, int&>*)vex::iterable::make_raw_range(vex::iterable::make_range(m_container)));
                Assert::IsTrue(ar);
            }
        }

        TEST_METHOD(intellisense_support_when_created_from_container)
        { // works partially
            VEX_TESTS_SCOPED_MEM_CHECK();

            {
                int t_container[] = { 1, 2, 3, 4 };
                auto r = vi::make_range(t_container);
                if (r.move_front())
                    r.front();

                if (r.size() > 0u)
                    r.at(0u);
            }

            {
                auto r = vi::make_range(m_container);
                if (r.move_front())
                    r.front();

                if (r.size() > 0u)
                    r.at(0u);
            }

            auto r1 = vi::make_any_range(m_container);

            if (r1->move_front())
                r1->front();

            if (r1->size() > 0u)
                r1->at(0u);

            auto r2 = std::move(r1);

            if (r2->move_back())
                r2->back();

            if (r2->size() > 0u)
                r2->at(0u);
        }

        TEST_METHOD(intellisense_support_when_created_from_range_of_iterators)
        {
            auto r1 = vi::make_any_range(m_container.begin(), m_container.end());

            if (r1->move_front())
                r1->front();

            if (r1->size() > 0u)
                r1->at(0u);

            auto r2 = std::move(r1);

            if (r2->move_back())
                r2->back();

            if (r2->size() > 0u)
                r2->at(0u);
        }

        TEST_METHOD(intellisense_support_when_created_from_range)
        { // works
            auto r1 = vi::make_any_range(vex::iterable::make_range(m_container));

            if (r1->move_front())
                r1->front();

            if (r1->size() > 0u)
                r1->at(0u);

            auto r2 = std::move(r1);

            if (r2->move_back())
                r2->back();

            if (r2->size() > 0u)
                r2->at(0u);
        }

        TEST_METHOD(intellisense_support_when_created_from_array)
        { // works
            int t_container[] = { 1, 2, 3, 4, 5 };
            auto r1 = vi::make_any_range(t_container);

            if (r1->move_front())
                r1->front();

            if (r1->size() > 0u)
                r1->at(0u);

            auto r2 = std::move(r1);

            if (r2->move_back())
                r2->back();

            if (r2->size() > 0u)
                r2->at(0u);

            {
                auto r = vex::as_any_from_output() | 
                    vex::iterable::make_raw_range(vex::iterable::make_range(m_container));
                Assert::IsTrue(r);
                Assert::IsTrue(r->move_front());
                Assert::AreEqual(m_container.front(), r->front());
                Assert::AreEqual(m_container.back(), r->back());
            }
        }

        TEST_METHOD(intellisense_support_when_output_range)
        {
            {
                auto r = vex::iterable::make_any_output_range<int>(std::back_inserter(m_container));
                if (r) {
                    r->put(1);
                }

                auto p = r.get();
                if (p) {
                    p->put(2);
                }

                container_t().swap(m_container);
            }
        }
    };
}}